/*
 *  Created on: Jun 13, 2013
 *  Author: Heinrich Strauss <heinrich@hstrauss.co.za>
 *  This software is licenced under the GPL v2.0 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>
 */

#include "h/zlecmp.h"
#include "h/zledefs.h"

inline void explode00s(char* p_input, char* p_output, uint64_t p_count){
	for (byte i = 0; i < p_count; ++i){
		p_output[i] = C_ZERO_MARKER;
	}

}

inline void explodeFFs(char* p_input, char* p_output){
	p_output[0] = C_MARKER;
}

//static
uint8_t zlecmp::zcompress  (const uint64_t p_block_index, const uint64_t p_blockSize, uint64_t *p_uncompressed_bytes, uint64_t* p_block_output_size_bytes, void* p_input, void* p_output){
#if (CONST_ALG == 1)
	{
		//snipped
	}
#elif (CONST_ALG == 2)
	{
		//snipped
	}
#elif (CONST_ALG == 3) // RLE - Dummy
	{
		//snipped
	}
#elif (CONST_ALG == 4) // RLE
	{
		//snipped
	}
#elif (CONST_ALG == 5) // ZLE Compressor
	{
		//snipped

	}
#elif (CONST_ALG == 6) // ZLE Compressor
	{
		//assert(p_input!=nullptr);
		//assert(p_output!=nullptr);
		uint64_t i_index  = 0;
		uint64_t c_loop = 0;
		uint64_t o_index = CONST_SIZE_C_FILE_HEAD; // offset to allow for uint64_t describing size of compressed block
		uint64_t c_repeat = 0;
		byte repeat_count_hit_this_run = 0;

		byte* input = (byte*)p_input;
		byte* output = (byte*)p_output;
		uint64_t *output64_t = (uint64_t*)p_output;

		//assert(input!=nullptr);
		//assert(output!=nullptr);
		//assert(output64_t!=nullptr);
		uint64_t zeroes = 0;

		byte char_limit;

		for(;i_index < p_blockSize-C_IGNORE_LAST_ENCODING;){

			char_limit = MIN(C_SIZE_LIMIT,p_blockSize-C_IGNORE_LAST_ENCODING-i_index);

			if (input[i_index] == C_MARKER){ // Embed a FF
				output[o_index] = C_MARKER;
				output[o_index+1] = C_ZERO_MARKER;
				o_index+=2;
				//assert (output[o_index-1] != C_MARKER);
				++i_index;
			}
			else if (input[i_index] == C_ZERO_MARKER) {// embed a run of zeroes

				for(c_loop = 0; c_loop < char_limit; c_loop++)
				{
					if(input[i_index]!=input[i_index+c_loop]) {// successor differs
						break;
					}

				}
				//assert(c_loop != 0);
				if(c_loop == 1){
					output[o_index] = C_ZERO_MARKER;
					++i_index;
					++o_index;
				}
				else if (c_loop == 2){
					output[o_index] = output[o_index+1]= C_ZERO_MARKER;
					i_index+=2;
					o_index+=2;
				}
				else{
					output[o_index] = C_MARKER;
					output[o_index+1] = c_loop;
					i_index+=c_loop;
					o_index+=2;
				}

			}
			else
			{
				output[o_index] = input[i_index]; // embed another character;
				++o_index;
				++i_index;
			}

		}
		//		printdb2("\nEnd of Run %lu", p_block_index);
		//		printdb2("\ni_index: %lu", i_index);
		//		printdb2("\no_index: %lu", o_index);
		//		printdb2("\np_blockSize: %lu", p_blockSize);

		//assert(i_index <= p_blockSize-C_IGNORE_LAST_ENCODING);
		//deal with last two chars
		// naively store last C_LAST_ENCODING chars
		for (;i_index < p_blockSize;){
			output[o_index] = input[i_index];
			++i_index;
			++o_index;
		}

		*p_block_output_size_bytes = o_index; //
		output64_t[0] = o_index;
		output64_t[1] = p_block_index;

	}
#endif


	return 0;
}
//static
uint8_t zlecmp::zdecompress(const uint64_t p_block_index, const uint64_t p_blockSize, const uint64_t p_output_blockSize,  uint64_t* p_block_output_size_bytes, void* p_input, void* p_output){
#if (CONST_ALG == 1)
	{
		//snipped
	}
#elif (CONST_ALG == 2)
	{
		//snipped
	}
#elif (CONST_ALG == 3) // RLE - Dummy
	{
		//snipped
	}
#elif (CONST_ALG == 4) // RLE
	{
		//snipped
	}
#elif (CONST_ALG == 5)
	{
		//snipped
	}
#elif (CONST_ALG == 6) // ZLE decompressor
	{

		byte* input = (byte*)(p_input);
		byte* output = (byte*)(p_output);
		uint64_t* output64_t = (uint64_t*)(p_input);

		//assert(output64_t !=nullptr);
		//assert(output64_t[0] != 0);

		//assert(sizeof(input[0]) ==1);
		//assert(sizeof(byte) ==1);
		//		printdb2("\nSize of input block: %lu\n",output64_t[0]);
		//		printdb2("Size of p_block: %lu\n",p_blockSize);
		//assert(output64_t[0] <= p_blockSize);

		uint64_t i_index = CONST_SIZE_C_FILE_HEAD; // index to be read; offset to char after size of p_input
		uint64_t o_index = 0; // index to be written
		uint64_t c_repeat = 0; // number of characters to be written

		//		printdb2(" for block %lu\n",p_block_index);
		for(i_index=CONST_SIZE_C_FILE_HEAD;i_index < output64_t[0]-C_IGNORE_LAST_DECODING;){

			if(input[i_index] == C_MARKER) {// either zeroes or FF

				if(input[i_index+1]==C_ZERO_MARKER) // FF
				{
					output[o_index] = C_MARKER;
					i_index++; // seek past marker
				}
				else // must be zeroes
				{
					for(c_repeat = 0;c_repeat < input[i_index+1];++c_repeat){ // explode zeroes
						output[o_index+c_repeat] = C_ZERO_MARKER;
					}
					o_index+=input[i_index+1]-1; // o_output is incremented at end
					++i_index;
				}
			}
			else // NOT A C_MARKER; normal char
			{
				output[o_index] = input[i_index];
				//				t_exploded_count +=1;
			}

			// INCREMENT BOTH COUNTERS
			++i_index;
			++o_index;
		}

		for (;i_index < p_blockSize;){
			output[o_index] = input[i_index];
			++i_index;
			++o_index;
		}

		*p_block_output_size_bytes = o_index;
	}
#endif

	return 0;
}

#if (CONST_ALG == 6)
uint8_t zlecmp::zcompress_mp  (const uint64_t p_block_index, const uint64_t p_blockSize, uint64_t *p_uncompressed_bytes, uint64_t* p_block_output_size_bytes, void* p_input, void* p_output){
	//	*p_block_output_size_bytes = p_blockSize;
	zlecmp::zcompress(p_block_index, p_blockSize, p_uncompressed_bytes, p_block_output_size_bytes, p_input, p_output);
	return 0;
}
uint8_t zlecmp::zdecompress_mp(const uint64_t p_block_index, const uint64_t p_blockSize, const uint64_t p_output_blockSize, uint64_t* p_block_output_size_bytes, void* p_input, void* p_output){
	//	*p_block_output_size_bytes = p_blockSize;
	zlecmp::zdecompress(p_block_index, p_blockSize, p_output_blockSize, p_block_output_size_bytes, p_input, p_output);
	return 0;
}

#endif



